use crate::{ExportAction, Info, SqlConfig};
use std::{
    collections::BTreeMap,
    fs::OpenOptions,
    io::{Seek, Write},
    path::Path,
};
pub(crate) struct Markdown {}

impl ExportAction for Markdown {
    fn run(
        config: &SqlConfig,
        groups: &BTreeMap<&String, Vec<&Info>>,
    ) -> Result<String, crate::RunError> {
        let output = &get_output(config);
        let path = Path::new(output);
        let mut file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(path)
            .unwrap();
        write(groups, &mut file).unwrap();
        Ok(output.to_string())
    }
}

pub(crate) fn write<T>(groups: &BTreeMap<&String, Vec<&Info>>, writer: &mut T) -> Result<(), ()>
where
    T: Write + Seek,
{
    let content = groups
        .iter()
        .enumerate()
        .map(|(index, (name, list))| {
            let title = write_title(index.to_string(), name);
            let table = write_table(list);
            format!("{}\n\n{}\n", title, table)
        })
        .collect::<Vec<_>>()
        .join("\n");
    writer.write_all(content.as_bytes()).unwrap();
    Ok(())
}

fn write_table(list: &[&Info]) -> String {
    let header = "|序号|列名|数据类型|为Null|备注|";
    let seprate_line = "|---|---|---|---|---|";
    let body = list
        .iter()
        .map(|info| {
            let Info {
                ordinal_position,
                data_type,
                is_nullable,
                column_name,
                column_comment,
                ..
            } = info;
            let column_comment = column_comment.as_ref().map(|v| v.as_str()).unwrap_or("");
            format!("|{ordinal_position}|{column_name}|{data_type}|{is_nullable}|{column_comment}|")
        })
        .collect::<Vec<String>>()
        .join("\n");
    format!("{header}\n{seprate_line}\n{body}\n<!-- end_slide -->\n")
}
fn write_title<T, U>(index: T, name: U) -> String
where
    T: AsRef<str>,
    U: AsRef<str>,
{
    format!("## {}.{}", index.as_ref(), name.as_ref())
}

/// 获取输出路径
pub(crate) fn get_output(config: &SqlConfig) -> String {
    let name = format!("{}.md", config.database);
    config
        .output
        .as_ref()
        .map(|v| {
            if v.ends_with(".md") {
                v.to_string()
            } else {
                format!("{}.md", v)
            }
        })
        .unwrap_or(name)
}
